/*
 * Copyright (c) 2018, apexes.net. All rights reserved.
 *
 *         http://www.apexes.net
 *
 */
package net.apexes.wsonrpc.json.support.jackson;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ContainerNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import net.apexes.wsonrpc.json.JsonRpcRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Type;

/**
 * @author <a href=mailto:hedyn@foxmail.com>HeDYn</a>
 */
class JacksonJsonRpcRequest implements JsonRpcRequest {

    private static final Logger LOG = LoggerFactory.getLogger(JacksonJsonRpcRequest.class);

    private final ObjectMapper objectMapper;
    private final ObjectNode objectNode;
    private final String id;
    private final String trace;
    private final String method;
    private final ContainerNode<?> params;

    JacksonJsonRpcRequest(ObjectMapper objectMapper, ObjectNode objectNode) {
        this.objectMapper = objectMapper;
        this.objectNode = objectNode;

        if (objectNode.has("id")) {
            JsonNode node = objectNode.get("id");
            if (node.isNull()) {
                this.id = null;
            } else {
                this.id = node.textValue();
            }
        } else {
            this.id = null;
        }
        JsonNode traceNode = objectNode.get("trace");
        this.trace = traceNode == null ? null : traceNode.textValue();
        this.method = objectNode.get("method").textValue();
        this.params = (ContainerNode<?>) objectNode.get("params");
    }

    @Override
    public String getId() {
        return id;
    }

    @Override
    public String getTrace() {
        return trace;
    }

    @Override
    public String getMethod() {
        return method;
    }

    @Override
    public int getParamCount() {
        if (params == null) {
            return 0;
        }
        return params.size();
    }

    @Override
    public Object getParams() {
        return params;
    }

    @Override
    public Object[] getParams(Type[] types) {
        if (params == null) {
            return new Object[0];
        }
        try {
            Object[] args = new Object[types.length];
            for (int i = 0; i < args.length; i++) {
                JsonNode param = params.get(i);
                if (param.isNull()) {
                    args[i] = null;
                } else {
                    args[i] = objectMapper.convertValue(param, objectMapper.constructType(types[i]));
                }
            }
            return args;
        } catch (Exception e) {
            LOG.debug("params error!", e);
            return null;
        }
    }

    @Override
    public String getParamsAsJson() {
        try {
            return objectMapper.writeValueAsString(params);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Object getParam(int index) {
        return params.get(index);
    }

    @Override
    public String getParamAsJson(int index) {
        try {
            return objectMapper.writeValueAsString(getParam(index));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String toJson() {
        try {
            return objectMapper.writeValueAsString(objectNode);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean isRequest() {
        return true;
    }

    @Override
    public boolean isResponse() {
        return false;
    }
}
